Java基础知识(八)

Author Avatar
子语 2017 - 10 - 14
  • 在其它设备中阅读本文章

对象比较

1.数字比较用==,字符串比较用equals(String str)

要进行对象比较,必须对对象中所有属性的值进行比较。

范例:对象比较的基本方式

class Book {
    private String title;
    private double price;

    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }

    //省略无参构造方法和setter方法
    public String getTitle() {
        return this.title;
    }

    public double getPrice() {
        return this.price;
    }
}

public class Demo {
    public static void main(String[] args) {
        Book bkA = new Book("Java开发", 79.5);
        Book bkB = new Book("JSP", 45.6);
        if (bkA.getTitle().equals(bkB.getTitle())
                && bkA.getPrice() == bkB.getPrice()) {
            System.out.println("bkA和bkB是同一个对象!");
        } else {
            System.out.println("bkA和bkB是不同对象!");
        }
    }
}

上述程序存在问题:主方法相当于客户端,客户端的程序逻辑应该越简单越好。所以对象比较应该由对象自己完成,即对象所对应的类应该提供对象比较的方法。

附:类的属性使用private封装,那么在类的外部就不能通过对象直接调用属性

class Info{
    private String msg = "Hello";
    public void print(){
        System.out.println("msg = " + this.msg);
    }
}
public class Demo {
    public static void main(String[] args) {
        Info x = new Info();
        x.msg = "sss"; // 报错,无法访问private属性
        x.print();
    }
}

附:将一个对象作为参数传回类的方法中,相当于取消封装,使得对象可以直接访问属性。

class Info {
    private String msg = "Hello";

    public void print() {
        System.out.println("msg = " + this.msg);
    }

    public void fun(Info temp) {
        // 在类的内部直接利用对象访问私有属性
        temp.msg = "修改内容";
    }
}

public class Demo {
    public static void main(String[] args) {
        Info x = new Info();
        x.fun(x);
        x.print(); // msg = 修改内容
    }
}

附:一个类接收本类对象形式的代码在对象比较时很常见。

class Book {
    private String title;
    private double price;

    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }

    //省略无参构造方法和setter方法
    public String getTitle() {
        return this.title;
    }

    public double getPrice() {
        return this.price;
    }

    // 类接收自身对象,对象可以直接访问属性,不需要getter方法
    // compare()有两个功能:一带回了要比较的信息;二方便属性访问
    public boolean compare(Book book) {
        if (book == null) { // 要比较的对象为空
            return false; // 没必要比较
        }
        if (this == book) { // 内存地址相同
            return true; //避免具体比较,节约时间
        }
        // 执行“bkA.equals(bkB)”时,有两个对象:
        // 一个为当前对象this(调用方法的对象,即bkA)
        // 一个为传递对象book(引用传递,即bkB)
        if (this.title.equals(book.title)
                && this.price == book.price) {
            return true;
        } else {
            return false;
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        Book bkA = new Book("Java开发", 79.5);
        Book bkB = new Book("JSP", 45.6);
        if (bkA.compare(bkB)) {
            System.out.println("bkA和bkB是同一个对象!");
        } else {
            System.out.println("bkA和bkB是不同对象!");
        }
    }
}

总结
(1)对象比较的方法必须定义在类中;
(2)对象比较时一定要判断是否为null,内存地址是否相同,属性是否相同。

关键字static

static 定义属性

问题引出:

class Book {
    private String title;
    private double price;
    // 为操作方便,暂不封装
    String pub = "清华大学出版社";

    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }

    public String getInfo(){
        return "书名:" + this.title + ",价格:" + this.price + ",出版社:" + this.pub;
    }
}

public class Demo {
    public static void main(String[] args) {
        Book bkA = new Book("Java开发", 79.5);
        Book bkB = new Book("JSP", 45.6);
        System.out.println(bkA.getInfo()); // 清华大学出版社
        System.out.println(bkB.getInfo()); // 清华大学出版社
        bkB.pub = "北京大学出版社";
        System.out.println(bkA.getInfo()); // 清华大学出版社
        System.out.println(bkB.getInfo()); // 北京大学出版社
    }
}

对上述代码进行内存分析:
无法加载
通过内存分析,发现属性重复:每个对象各自占有相同的属性值。假如有1000个该对象,要修改所有对象的pub属性,就需要分别进行修改。因此如果将pub属性定为普通属性,那每个堆内存都保存有各自的pub值。

1.但所有对象的pub值都一样,应将其定为一个共享的属性,即所有对象都指向同一块pub属性。可以利用static定义共享属性。

class Book {
    private String title;
    private double price;
    // 为操作方便,暂不封装
    static String pub = "清华大学出版社";

    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }

    public String getInfo(){
        return "书名:" + this.title + ",价格:" + this.price + ",出版社:" + this.pub;
    }
}

public class Demo {
    public static void main(String[] args) {
        Book bkA = new Book("Java开发", 79.5);
        Book bkB = new Book("JSP", 45.6);
        System.out.println(bkA.getInfo()); // 清华大学出版社
        System.out.println(bkB.getInfo()); // 清华大学出版社
        bkB.pub = "北京大学出版社";
        System.out.println(bkA.getInfo()); // 北京大学出版社
        System.out.println(bkB.getInfo()); // 北京大学出版社
    }
}

使用static定义属性后,只要有一个对象修改属性值,那么所有对象的该属性值都会改变,内存分析如下:
无法加载
2. static定义的属性与普通属性区别在于保存数据的内存区域不同。
static定义的是公共属性,任由某个对象直接修改属性值是不合理的,应该由所有对象的代表进行属性访问,即用类访问。因此static定义的属性,可直接用类名调用:Book.pub = "北京大学出版社";
3. static属性与普通属性的区别在于:普通属性必须由实例化的对象调用,而在没有实例化对象的情况下,static属性依然可以被调用。

public class Demo {
    public static void main(String[] args) {
        // 没有实例化对象的情况下,输出pub的内容
        System.out.println(Book.pub);
    }
}

由结果可知:static虽然定义在类中,但不受对象控制,是独立于类存在的。
4. 何时使用static定义属性
编写类时,static不是首选修饰符,当需要描述共享信息时,才使用static,方便集体修改,不用重复开辟内存空间。

static 定义方法

1.static定义的方法也可以直接被类名调用

class Book {
    private String title;
    private double price;
    // 为操作方便,暂不封装
    private static String pub = "清华大学出版社";

    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }

    public static void setPub(String p){
        pub = p;
    }
    public String getInfo(){
        return "书名:" + this.title + ",价格:" + this.price + ",出版社:" + this.pub;
    }
}

public class Demo {
    public static void main(String[] args) {
        // 没有实例化对象的情况下,调用方法
        Book.setPub("北京大学出版社");
        Book bkA = new Book("Java开发", 79.5);
        System.out.println(bkA.getInfo()); // 北京大学出版社
    }
}

上述代码存在问题:类中有两种方法,static方法和普通方法,两种方法间的访问受到限制。
(1)static方法不能直接使用非static属性或方法,只能调用static属性或方法。

public static void setPub(String p){
   pub = p;
   title = "sss"; // 报错,无法引用非静态变量
   getInfo(); // 报错,无法引用非静态方法
   System.out.println(this); // 报错,无法引用非静态变量
}

(2)普通方法可以使用static属性或方法

public String getInfo(){
    setPub(""); // 不报错
    return ",出版社:" + this.pub; // 不报错
}

出现上述限制的原因:

|- 普通属性和方法必须在对象实例化后分配了堆内存空间,才可以使用;
|- static定义的方法和属性,不受实例化对象控制,可在没有实例化对象情况下访问。

2.一个方法定义在主类中,并由主方法直接调用,该方法定义格式如下:

public static 返回值类型 方法名(参数类型 参数, 参数类型 参数,...) {
    方法体; 
    [return [返回值] ;] // []中内容可写可不写
}

一个方法定义在类中,由对象直接调用,其语法格式如下:

public 返回值类型 方法名(参数类型 参数, 参数类型 参数,...) {
    方法体; 
    [return [返回值] ;] // []中内容可写可不写
}

观察代码:

public class Demo {
    public static void main(String[] args) {
        fun();
    }

    public static void fun() {
        System.out.println("Hello World !");
    }
}

没有static定义的fun()必须通过对象调用,主方法要使用fun()必须实例化对象

public class Demo {
    public static void main(String[] args) {
        // 产生对象,再利用对象调用非static方法
        new Demo().fun();
    }

    public void fun() {
        System.out.println("Hello World !");
    }
}
  1. 定义类中方法时,static不是首选修饰符,因为每个对象可以利用自己的属性实现方法的不同调用。
class Flag {
    private boolean flag;

    public Flag(boolean flag) {
        this.flag = flag;
    }

    public void fun() {
        if (this.flag) {
            System.out.println("可以操作");
        } else {
            System.out.println("不可以操作");
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        Flag fA = new Flag(true);
        Flag fB = new Flag(false);
        fA.fun();
        fB.fun();
    }
}

当一个类中没有属性,只有方法时,建议将所有方法定义为static方法。这样就不用每次调用时都需要有实例化对象。

class MyMath {
    public static int add(int x, int y) {
        return x + y;
    }
}

public class Demo {
    public static void main(String[] args) {
        System.out.println(MyMath.add(10, 20));
    }
}

主方法

1.主方法的组成:

组成 作用
public 主方法是程序的开始,所以主方法必须是可见、public(公共的)的
static 证明此方法可直接由类名调用
void 主方法是程序的开始,因此不能回头,执行完为止,所以不能有返回值
main 系统规定好的方法名,不能修改
String [] args 指的是程序运行时传递的参数

范例:对主方法传入参数

public class Demo {
    public static void main(String[] args) {
        for (int x = 0; x < args.length ; x++) {
            System.out.println(args[x]); // 未输出参数,为空
        }
    }
}

多个参数时,必须使用空格分割。cmd执行的是java Demo 1 3 4 6
输出为:1 3 4 6
如果参数本身带有空格,需用""描述
cmd执行的是java Demo "Hello world" "Hello Java"

static应用案例

已知:
(1)多个对象,都使用同一个static属性;
(2)static定义方法可以避免实例化对象调用方法的限制。

1.实现对实例化对象个数的统计

要求:每实例化一个对象,就输出"这是第x个实例化对象"
思路:每次实例化对象,就会调用构造方法,因此可在构造方法中增加一个统计数据的操作,每当新对象产生,该属性值就自增加一。

class Book {
    private static int num = 0;

    public Book() {
        num++;
        System.out.println("这是第" + num + "个实例化对象");
    }
}

public class Demo {
    public static void main(String[] args) {
        new Book();
        new Book();
        new Book();
    }
}

2.实现属性的自动设置

要求:类中有一个无参构造方法,一个有参构造方法,有参构造方法的功能是传递title值。不论调用的哪个构造方法,均可为title赋值,且属性值尽量不重复。

class Book {
    private String title;
    private static int num = 0;

    public Book() {
        this("Title:No." + num++);
    }
    public Book(String title){
        this.title = title;
    }
    public String getTitle(){
        return  this.title;
    }
}

public class Demo {
    public static void main(String[] args) {
        System.out.println(new Book("Java开发").getTitle()); // Java开发
        System.out.println(new Book().getTitle()); // Title:No.0
        System.out.println(new Book().getTitle()); // Title:No.1
    }
}

总结
(1)类定义属性或方法首时选不是static属性或方法;
(2)static属性或方法可直接用类名调用;
(3)static属性保存在全局数据区。
(4)内存区有四种: 栈内存(对象的地址),堆内存(普通属性),全局数据区(static属性),全局代码区(所有的方法)

This blog is under a CC BY-NC-SA 3.0 Unported License
本文链接:http://yov.oschina.io/article/Java/Java Base/Java基础知识(八)/